/*
 * Copyright (C) 2010 Fores Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jp.fores.midori.client.caller;

import java.util.List;

import jp.fores.midori.client.RPCClient;
import jp.fores.midori.client.exception.AllTargetRPCFailureException;
import jp.fores.midori.server.exception.MidoriServerUnRecoveryException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * 複数のサーバーに対して順番にリモートプロシージャーコールを行うようにするための実装クラス。<br>
 * (このクラスの全てのメソッドはスレッドセーフです。)<br>
 */
public class SequenceRPCClientCaller extends AbstractRPCClientCaller {
	//==========================================================
	//定数

	/**
	 * ログ出力用
	 */
	private static final Log log = LogFactory.getLog(SequenceRPCClientCaller.class);


	//==========================================================
	//フィールド

	/**
	 * リモートプロシージャーコールを行うクライアントクラス
	 */
	protected final RPCClient rpcClient;


	//==========================================================
	//メソッド

	//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	//コンストラクタ

	/**
	 * コンストラクタです。<br>
	 *
	 * @param rpcClient リモートプロシージャーコールを行うクライアントクラス
	 */
	public SequenceRPCClientCaller(RPCClient rpcClient) {
		//==========================================================
		//引数の値をフィールドに設定

		//リモートプロシージャーコールを行うクライアントクラス
		this.rpcClient = rpcClient;
	}


	//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	//抽象メソッドの実装

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object remoteCall(URLListGetter urlListGetter, String methodName,
			Object... args) throws Exception {
		//==========================================================
		//サーバーのURLのリストを取得
		List<String> urlList = urlListGetter.getURLList();

		//デバッグログが有効な場合
		if (log.isDebugEnabled()) {
			//デバッグログ出力
			log.debug("取得したサーバーのURLのリスト:" + urlList);
		}

		//サーバーのURLのリストが取得できなかった場合
		if ((urlList == null) || (urlList.isEmpty())) {
			//全てのRPCの対象に対して呼び出しが失敗した場合の例外を投げる
			throw new AllTargetRPCFailureException("サーバーのURLのリストが取得できませんでした。");
		}


		//==========================================================
		//取得したサーバーのURLのリストに対して順番にリモートプロシージャーコールを行う
		for (String url : urlList) {
			//実行中のスレッドに割り込みが発生している場合
			//(割り込みを検知する処理を入れておかないと、いつまでたってもスレッドが停止しないことがあるため)
			if (Thread.currentThread().isInterrupted()) {
				//デバッグログが有効な場合
				if (log.isDebugEnabled()) {
					//デバッグログ出力
					log.debug("複数のサーバーに対して順番にリモートプロシージャーコールを行っている途中で、リモートプロシージャーコールの途中で割り込みを検知しました。");
				}

				//割り込みが発生した場合の例外を投げる
				throw new InterruptedException("複数のサーバーに対して順番にリモートプロシージャーコールを行っている途中で、割り込みが発生しました。");
			}

			try {
				//実際にリモートプロシージャーコールを行い、結果をそのまま返す
				return this.rpcClient.remoteCall(url, methodName, args);
			}
			//サーバー側で回復不能な例外が発生した場合
			catch (MidoriServerUnRecoveryException e) {
				//デバッグログが有効な場合
				if (log.isDebugEnabled()) {
					//デバッグログ出力
					log.debug("サーバー側で回復不能な例外が発生しました。");

					//捕捉した例外をそのまま投げる
					//(これ以上不毛な処理を行わないようにするため)
					throw e;
				}
			}
			//それ以外の例外が発生した場合
			catch (Exception e) {
				//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
				//処理を継続させるために、デバッグログを出力するだけで例外をもみ消す

				//デバッグログが有効な場合
				if (log.isDebugEnabled()) {
					//デバッグログ出力
					log.debug("リモートプロシージャコールの途中で例外が発生しました。", e);
				}
			}
		}

		//全てのRPCの対象に対して呼び出しが失敗した場合の例外を投げる
		throw new AllTargetRPCFailureException("全てのサーバーのURLに対してリモートプロシージャーコールを行ないましたが、全て失敗しました。");
	}
}
